$(function () {
    const CODE = {
        200: '200 OK',
        201: '201 ACCEPT',
        404: '404 NOT FOUND',
        500: '500 INTERNAL ERROR'
    }, FIELD_NAMES = {
        'gw-name': '名称',
        'gw-uri': '访问路径',
        'gw-method': '请求方式',
        'gw-code': '应答码',
        'gw-type':'应答类型',
        'gw-value': '返回值模板',
        'gw-remark': '备注'
    }, TABLE_OPTS = {
        container: '#response-headers', columns: 2, rows: 0, commands: null,
        selectable: true,
        tableColumns: [
            {header: '名称', className: 'http-header-name'},
            {header: '值', className: 'http-header-value'}
        ],
        onRowSelect: function () {
            $('.http-header-button').removeClass ('disabled');
        },
        onRowUnselect: function () {
            let a = this.getSelectedRowIndexes ();
            if (!a || !a.length) {
                $('.http-header-button[data-role=delete]').addClass ('disabled');
            }
        }
    };
    let holder = {
            controls: {
                name: $('#gw-name'),
                code: $('#gw-code'),
                method: $('#gw-method'),
                type: $('#gw-type'),
                remark: $('#gw-remark'),
                id: $('#gw-id'),
                uri: $('#gw-uri'),
                value: $('#gw-value'),

                clear: function () {
                    holder.fields.forEach (c => {
                        clear_error (c);
                        c.val ('');
                    });
                }
            },
            pagination: {
                pn: 1, ps: 1, more: true
            }
        },
        alerts = (function () {
            let listener, processor = function () {
                    try {
                        if (listener) {
                            listener ();
                        }
                    } finally {
                        listener = undefined;
                        root.modal ('hide');
                    }
                };
            let main = $('#alerts-message'),
                root = $('#alerts').on ('hidden.bs.modal', () => main.text ('')),
                ok = $('#alerts-ok').on ('click', processor),
                yes = $('#alerts-yes').on ('click', processor),
                no = $('#alerts-no');

            return {
                message: function (message, callback) {
                    listener = callback;
                    main.text (message);
                    ok.show ();
                    yes.hide ();
                    no.hide ();
                    root.modal ('show');
                },
                confirm: function (message, callback) {
                    listener = callback;
                    main.text (message);
                    ok.hide ();
                    yes.show ();
                    no.show ();
                    root.modal ('show');
                }
            }
        }) (),
        invoker = (function () {
            let root = $('#invoker'), panel = $('#invoke-param-panel'),
                code = $('#result-code'), type = $('#result-type'), content = $('#invoke-result-content'),
                uri = $('#invoker-uri'), cache = {};
            root.on ('hidden.bs.modal', () => {
                if (cache.item) {
                    delete cache.item;
                    console.log ('cache.item deleted');
                }
                if (cache.params) {
                    delete cache.params;
                    console.log ('cache.params deleted');
                }
                $('#invoke-response-headers').empty ();
            });

            function fill_data (data) {
                code.removeClass ('text-danger text-success').empty ();
                content.empty ();

                if (data.code !== 200 && data.code !== 201) {
                    code.addClass ('text-danger');
                } else {
                    code.addClass ('text-success');
                }
                code.text (CODE [data.code]);
                type.text (data.contentType);
                if (data.headers) {
                    $('#invoke-response-headers').text (JSON.stringify (data.headers, null, 4));
                }
                if (data.content) {
                    if (data.contentType.includes ('json')) {
                        try {
                            let o = JSON.parse (data.content);
                            content.text (JSON.stringify (o, null, 4));
                        } catch (E) {
                            console.error (E)
                        }
                    } else {
                        content.text (data.content);
                    }
                }
            }

            function remote_call (opts, callback) {
                let xhr = new XMLHttpRequest ();
                // console.log ('trying to open ' + opts.url + ' using ' + opts.type + '...');
                xhr.open (opts.type, opts.url, true);
                xhr.onload = function () {
                    let txt_headers = this.getAllResponseHeaders ();
                    let headers, append = false;
                    if (txt_headers !== '') {
                        headers = {};
                        txt_headers.split ('\r\n').forEach (p => {
                            let a = p.split (':'), name = $.trim (a[0]), value = $.trim (a[1]);
                            if (name) {
                                headers [name] = value;
                                append = true;
                            }
                        });
                    }

                    if (callback) {
                        let args = {
                            code: this.status,
                            content: this.responseText,
                            contentType: this.getResponseHeader ('Content-Type')
                        }
                        if (append) {
                            args.headers = headers;
                        }
                        // console.log ('args = ', args);
                        callback (args);
                    }
                }
                xhr.send ();
            }

            $('#btn-invoke').on ('click', function () {
                if (!cache.item) {
                    return;
                }

                let opts = {
                    url: cache.item.uri,
                    type: cache.item.method
                };
                if (opts.url.includes ('${')) {
                    // 有参数，寻找参数
                    let array = opts.url.split ('/');
                    for (let i = 0, n = array.length; i < n; i ++) {
                        let p = array[i];
                        if (/^\$\{(.*?)}$/.test (p)) {
                            let name = /^\$\{(.*?)}$/.exec (p) [1],
                                control = panel.find ('input[name="' + name + '"]'),
                                value = control.val ();
                            if (!value) {
                                attach_error (control, '请填写参数' + name);
                                return;
                            }
                            array[i] = value;
                        }
                    }
                    opts.url = array.join ('/');
                }

                opts.url = ('mock/' + opts.url).replace ('//', '/');
                remote_call (opts, fill_data);
            });
            function clean () {
                clear_error ($(this));
            }
            return {
                show: item => {
                    root.modal ('show');
                    panel.empty ();
                    code.empty ();
                    content.empty ();
                    type.empty ();
                    uri.text (item.uri);
                    if (item.uri.includes ('${')) {
                        let table = $('<table class="param-table"/>').appendTo (panel);
                        cache.params = {};
                        item.uri.split ('/').forEach (p => {
                            if (/^\$\{(.*?)}$/.test (p)) {
                                let name = /^\$\{(.*?)}$/.exec (p)[1],
                                    tr = $('<tr/>').appendTo (table), td;
                                $('<td class="param-name"/>').text (name).appendTo (tr);
                                td = $('<td/>').appendTo (tr);
                                $('<input type="text" class="form-control"/>')
                                    .on ('focus', clean)
                                    .attr ({name:name,id:name}).appendTo (td);
                                tr = $('<tr/>').appendTo (table);
                                $('<td/>').appendTo (tr);
                                $('<small class="text-danger">&nbsp;</small>')
                                    .attr ('id', name + '-hint')
                                    .appendTo ($('<td/>').appendTo (tr));
                            }
                        });
                    }
                    cache.item = item;
                },
            }
        })(),
        ul = $('#gateway-list'),
        dialog = $('#dialog'),
        title = $('#dialog-title'),
        helpTable = $('#hint-table');
    holder.fields = [
        holder.controls.name, holder.controls.code,
        holder.controls.method, holder.controls.type,
        holder.controls.remark, holder.controls.id,
        holder.controls.uri, holder.controls.value
    ];

    function buildList (append) {
        if (!append) ul.empty ();
        if (holder.data && holder.data.length) {
            holder.data.forEach (item => buildItem (item, ul));
            $('.inline-button.clickable').on ('click', on_icon_button_clicked);
        }
    }

    function buildItem (item, ul) {
        let bean = Object.assign ({}, item), s = '';
        if (bean.headers && bean.headers.toString () !== '{}') {
            for (let key in bean.headers) if (bean.headers.hasOwnProperty (key)) {
                s += Template.replace ({key: key, value: bean.headers[key]}, TEMPLATES.index.header);
            }
            if (s !== '') {
                s = Template.replace ({headers: s}, TEMPLATES.index["http-headers"]);
            }
        }

        bean.httpHeaders = s;
        if (bean.value && typeof bean.value) {
            let temp = JSON.parse (bean.value);
            bean.value = JSON.stringify (temp, null, 2);
        }
        Template.build (bean, TEMPLATES.index["dummy-interface"]).appendTo (ul);
    }

/*
    function buildItem (item, ul) {
        let li = $('<li class="gateway-item flexable"/>').attr ('data-uri', item.uri).appendTo (ul),
            method = item.method.toLowerCase();
        $('<div class="gateway-method flexable"/>').addClass ('gwm-' + method).text (item.method).appendTo (li);
        let div = $('<div class="auto-grow column-mode padding-15"/>').appendTo (li),
            line = $('<div class="flexable"/>').appendTo (div);
        $('<div class="gateway-name auto-grow"/>').text (item.name).appendTo (line);
        let p = $('<div/>').appendTo (line),
            a = $('<a class="inline-button copyable" data-role="copy" title="Copy URI"/>')
                .attr ('data-clipboard-text', item.uri).appendTo (p);
        $('<i class="fa fa-fw fa-copy"/>').appendTo (a);
        a = $('<a class="inline-button" data-role="edit" title="Edit"/>').on ('click', on_icon_button_clicked).appendTo (p);
        $('<i class="fa fa-fw fa-edit"/>').appendTo (a);
        a = $('<a class="inline-button" data-role="delete" title="Remove"/>').on ('click', on_icon_button_clicked).appendTo (p);
        $('<i class="fa fa-fw fa-trash"/>').appendTo (a);
        a = $('<a class="inline-button"  data-role="call" title="Try Me"/>').on ('click', on_icon_button_clicked).appendTo (p);
        $('<i class="fa fa-fw fa-play"/>').appendTo (a);
        a = $('<a class="inline-button"  data-role="history" title="Show History"/>').attr ('href', 'request-logs.html?if-id=' + item.id).appendTo (p);
        $('<i class="fa fa-fw fa-history"/>').appendTo (a);
        $('<div class="gateway-uri"/>').text (item.uri).appendTo (div);
        let flexable = $('<div class="flexable"/>').appendTo (div),
            left = $('<div class="auto-grow column-mode left-panel"/>').appendTo (flexable),
            right = $('<div class="auto-grow column-mode right-panel"/>').appendTo (flexable);
        div = $('<div class="line"/>').appendTo (left);
        $('<span class="gateway-label">应答类型: </span>').appendTo (div);
        $('<span class="gateway-return-type"/>').text (item.type).appendTo (div);
        if ('value' in item) {
            div = $('<div class="gateway-value"/>').appendTo (left);
            let obj = typeof item.value === 'string' ?
                    item.value === '' ? '' : JSON.parse (item.value) : item.value,
                text = JSON.stringify (obj, null, 4);
            $('<span class="gateway-label">返回值模板: </span>').appendTo (div);
            $('<pre/>').text (text).appendTo (div);
        }

        div = $('<div class="line"/>').appendTo (right);
        $('<span class="gateway-label">应答码: </span>').appendTo (div);
        $('<span class="gateway-code"/>').text (CODE[item.code]).appendTo (div);
        if ('remark' in item) {
            $('<div class="form-text text-muted"/>').html ('备注: ' + item.remark).appendTo (right);
        }
    }
*/

    function on_icon_button_clicked () {
        let self = $(this), uri = self.closest ('li').attr ('data-uri'),
            item = holder.byUri [uri], role = self.attr ('data-role');
        switch (role) {
            case 'edit':
                fill (item);
                dialog.modal ('show');
                break;

            case 'delete':
                delete_item (item);
                break;

            case 'call':
                invoker.show (item);
                break;

            case 'show-more':
                showMore (self);
                break;
        }
    }

    function showMore (sender) {
        let li = sender.closest ('li'), icon = sender.find ('.fa'), container = li.find ('.collapsable');
        container.slideToggle (() => {
            if (container.hasClass ('collapsed')) {
                container.removeClass ('collapsed');
                icon.removeClass ('fa-angle-double-right').addClass ('fa-angle-double-down');
            } else {
                container.addClass ('collapsed');
                icon.removeClass ('fa-angle-double-down').addClass ('fa-angle-double-right');
            }
        });
    }

    function checkTheForm () {
        let valid = check_control_empty (holder.controls.name) &&
                    check_control_empty (holder.controls.uri);
        if (!valid) {
            return;
        }

        let content = holder.controls.value.val (), o;
        if (content) try {
            if (content.includes ('${imei}')) {
                if (!holder.controls.uri.val ().includes ('${imei}')) {
                    attach_error (holder.controls.value, '变量 imei 未定义，您应该在 “访问路径” 中定义');
                    return;
                }
            }
            o = JSON.parse (content);
            content = JSON.stringify (o);
            if (content === '{}')   // 空对象
                content = undefined;
        } catch (E) {
            console.error (E);
            attach_error (holder.controls.value, '无效的JSON格式');
            return;
        }

        return {
            id: holder.controls.id.val (),
            name: holder.controls.name.val (),
            method: holder.controls.method.val (),
            code: holder.controls.code.val (),
            type: holder.controls.type.val (),
            remark: holder.controls.remark.val (),
            uri: holder.controls.uri.val (),
            value: content
        };
    }

    function check_control_empty (control) {
        if ('' === control.val ()) {
            attach_error (control, '请填写' + FIELD_NAMES[control.attr ('id')]);
            return false;
        }
        return true;
    }

    function attach_error (control, message) {
        let id = control.attr ('id'), hint = $('#' + id + '-hint');
        if (hint.length) {
            hint.text (message);
        }
        control.addClass ('border border-danger');
    }

    function clear_error (control) {
        let id = control.attr ('id'), hint = $('#' + id + '-hint');
        if (hint.length) {
            hint.html ('&nbsp;');
        }
        control.removeClass ('border border-danger');
    }

    function fill (item) {
        holder.controls.id.val (item.id);
        holder.controls.name.val (item.name);
        holder.controls.method.val (item.method);
        holder.controls.code.val (item.code);
        holder.controls.type.val (item.type);
        holder.controls.remark.val (item.remark);
        holder.controls.uri.val (item.uri);
        if (item.value) {
            let o = JSON.parse (item.value);
            holder.controls.value.val (JSON.stringify (o, null, 4));
        }
        dispose_table ();

        // holder.target = item.headers || [];
        if (item.headers) {
            holder.target = [];
            for (let key in item.headers) {
                if (item.headers.hasOwnProperty (key)) {
                    holder.target.push ({key: key, value: item.headers[key]});
                }
            }
        }
        install_table ();
    }

    function install_table () {
        holder.table = EditableTable.install (TABLE_OPTS);
        holder.model = holder.table.getModel ();
        if (!('target' in holder)) {
            holder.target = [];
        }

        attach_table_model ();
    }

    function dispose_table () {
        if (holder.table) {
            // 先销毁老的表格
            holder.table.dispose ();
            delete holder.table;
        }
        if (holder.model) {
            delete holder.model;
        }
        if (holder.target) {
            delete holder.target;
        }
    }

    function attach_table_model () {
        holder.model.getRowCount = function () {
            return holder.target ? holder.target.length : 0;
        };
        holder.model.getValueAt = function (row, column) {
            let obj = holder.target ? holder.target [row] : undefined;
            if (!obj) return undefined;

            switch (column) {
                // case 0: return '';
                case 0: return obj.key;
                case 1: return obj.value;
            }
        }
        holder.model.setValueAt = function (row, column, value) {
            let obj = holder.target ? holder.target [row] : undefined;
            if (obj) switch (column) {
                case 0: obj.key = value; break;
                case 1: obj.value = value; break;
            }
        }
        holder.model.fireTableDataChanged ();
    }

    function delete_item (item) {
        alerts.confirm ('您确定要删除这个接口吗？', () => {
            $.ajax ({
                url: 'backend/delete?id=' + item.id, type: 'delete', dataType: 'json'
            }).then (res => {
                if (res.success) load_data ();
            });
        });
    }

    function load_data (append) {
        if (!append) {
            holder.pagination = {pn: 1, ps: 30, more: true};
            ul.empty ();
        }
        let name = $.trim ($('#api-name').val ()), p = {
            pageNo: holder.pagination.pn,
            pageSize: holder.pagination.ps
        };
        if (name) {
            p.name = name;
        }
        $.get ('backend/list', p).then (res => {
            // console.log (res);
            if (res.data && res.data.length) {
                holder.data = res.data;
                res.data.forEach (di => {
                    if (di.headers) {
                        try {
                            di.headers = JSON.parse (di.headers);
                        } catch (E) {
                            console.warn (E);
                        }
                    }
                });
                holder.byUri = {};
                res.data.forEach (item => holder.byUri[item.uri] = item);
                buildList (append);

                if (res.data.length < holder.pagination.ps) {
                    holder.pagination.more = false;
                    $('#show-more-apis').hide ();
                }
                holder.pagination.pn ++;
            }
        });
    }

    function update_delete_header_button_status () {
        if (!holder.table) return;
        let a = holder.table.getSelectedRowIndexes (), button = $('.http-header-button[data-role=delete]');
        if (a && a.length) {
            button.removeClass ('disabled');
        } else if (!button.hasClass ('disabled')) {
            button.addClass ('disabled');
        }
    }

    $('#btn-ok').on ('click', function () {
        // console.log (holder.target);

        let item = checkTheForm (), create = holder.controls.id.val () === '';
        if (item) {
            if (holder.target) {
                let headers = {};
                holder.target.forEach (h => headers[h.key] = h.value);
                item.headers = JSON.stringify (headers);
            }
            // console.log (item);

            if (!item.method) {
                item.method = 'PUT';
            }
            if (!item.code) {
                item.code = 200;
            }
            if (!item.type) {
                item.type = 'application/json;charset=utf-8';
            }

            $.ajax ({
                url: create ? 'backend/create' : 'backend/update',
                type: 'post',
                dataType: 'json',
                data: JSON.stringify (item)
            }).then (res => {
                // console.log (res);
                if (res.success) {
                    load_data ();
                    dialog.modal ('hide');
                } else {
                    if ('field' in res) {
                        attach_error (holder.controls [res.field], res.message);
                    }
                }
            });
        }
    });

    function do_search () {
        holder.pagination.pn = 1;
        holder.pagination.more = true;
        ul.empty ();
        load_data ();
    }

    $('#show-more-apis').on ('click', () => load_data (true));
    $('#btn-search').on ('click', do_search);
    $('#api-name').on ('keyup', e => {
        if (e.keyCode === 13) {
            do_search ();
        }
    });

    dialog.on ('hidden.bs.modal', () => {
        title.text ('');
        holder.controls.clear ();
        $('#response-headers').empty ();
        dispose_table ();
    }).on ('show.bs.modal', () => {
        install_table ();
    });
    $('#btn-show-help').on ('mouseover', function () {
        helpTable.show ();
    }).on ('mouseout', function () {
        helpTable.hide ();
    });
    $('.http-header-button').on ('click', function () {
        let self = $(this), role = self.attr ('data-role');
        if (self.hasClass ('disabled')) {
            return;
        }
        if ('add' === role) {
            if (!holder.target) {
                holder.target = [{}];
            } else {
                holder.target.push ({});
            }
            holder.model.update ();
        } else if ('delete' === role) {
            let table = holder.table, selected = table.getSelectedRowIndexes ();
            if (selected && selected.length) {
                let message = '您确定要删除这' + (selected.length === 1 ? '个':'些') + "选中的行吗？";
                alerts.confirm (message, () => {
                    let backup = [], mark = {};
                    selected.forEach (i => mark[i] = i);
                    for (let i = 0, n = holder.target.length; i < n; i ++) {
                        if (!(i in mark)) {
                            backup.push (holder.target[i]);
                        }
                    }
                    delete holder.target;
                    holder.target = backup;
                    holder.model.update ();
                    update_delete_header_button_status ();
                });
            }
        }
    });
    holder.fields.forEach (c => c.on ('focus', ()=>clear_error (c)));
    load_data ();

    $.Toast.init ({
        direction: $.Toast.Layout.SOUTH | $.Toast.Layout.EAST,
        interval: 300, duration: 500
    });

    (function () {
        let pos = location.href.lastIndexOf ('/');
        $ ('#btn-copy-url').attr ('data-clipboard-text', location.href.substring (0, pos) + '/mock');
    })();

    (new ClipboardJS('.copyable')).on ('success', function (e) {
        $.Toast.show ('复制成功');
    });

    $(window).scroll (function(){
        let top = $(window).scrollTop();    //滚动条距顶部距离(页面超出窗口的高度)
        let total = parseFloat($(window).height()) + parseFloat(top);
        if(($(document).height()) <= total && holder.pagination.more) {
            load_data (true);
        }
    });
});